import os
import random

import cv2
import numpy as np
from PIL import Image
from PyQt5.QtGui import QPixmap, QPainter, QColor, QPen, QBrush
from PyQt5.QtCore import pyqtSignal, Qt, QPointF, QPoint
from PyQt5.QtWidgets import QWidget, QMenu, QApplication

from libs.shape import Shape
from libs.lib import distance

# 鼠标样式
CURSOR_DEFAULT = Qt.ArrowCursor
CURSOR_POINT = Qt.PointingHandCursor
CURSOR_DRAW = Qt.CrossCursor
CURSOR_MOVE = Qt.ClosedHandCursor
CURSOR_GRAB = Qt.OpenHandCursor


# class Canvas(QGLWidget):


class Canvas(QWidget):
    # 设置主文件信号
    zoomRequest = pyqtSignal(int)
    scrollRequest = pyqtSignal(int, int)
    newShape = pyqtSignal()
    selectionChanged = pyqtSignal(bool)
    shapeMoved = pyqtSignal()
    drawingPolygon = pyqtSignal(bool)

    CREATE, EDIT = list(range(2))

    epsilon = 4.0

    def __init__(self, *args, **kwargs):
        super(Canvas, self).__init__(*args, **kwargs)
        # Initialise local state.
        self.mode = self.EDIT
        self.shapes = []
        self.current = None
        self.selectedShape = None  # save the selected shape here
        self.selectedShapeCopy = None
        self.drawingLineColor = QColor(0, 255, 0)
        self.drawingRectColor = QColor(0, 255, 0)
        self.line = Shape(line_color=self.drawingLineColor)
        self.type = Shape(line_color=self)
        self.prevPoint = QPointF()
        self.offsets = QPointF(), QPointF()
        self.scale = 1.0
        self.pixmap = QPixmap()  # 加载的图片
        self.visible = {}
        self._hideBackround = False
        self.hideBackround = False
        self.hShape = None
        self.hVertex = None
        self._painter = QPainter()
        self._cursor = CURSOR_DEFAULT
        # Menus:
        self.menus = (QMenu(), QMenu())
        # Set widget options.
        self.setMouseTracking(True)
        self.setFocusPolicy(Qt.WheelFocus)
        self.verified = False
        # 橡皮擦
        self.r = 0
        self.g = 0
        self.b = 0
        self.a = 255
        self.lastPoint = QPoint()  # 起始点
        self.endPoint = QPoint()  # 终点
        self.maskDirPath_if = None

        self.shapesBackups = []
        self.movingShape = False
        self.hShape = None

        # Geoffrey
        self.scaledImg = self.pixmap.scaled(self.size())  # 初始化缩放图
        self.singleOffset = QPoint(0, 0)  # 初始化偏移值

        self.isLeftPressed = False
        self.isImgLabelArea = True
        # Geoffrey

    def storeShapes(self):
        shapesBackup = []
        for shape in self.shapes:
            shapesBackup.append(shape.copy())
        if len(self.shapesBackups) >= 10:
            self.shapesBackups = self.shapesBackups[-9:]
        self.shapesBackups.append(shapesBackup)

    @property
    def isShapeRestorable(self):
        if len(self.shapesBackups) < 2:
            return False
        return True

    def restoreShape(self):
        if not self.isShapeRestorable:
            return
        self.shapesBackups.pop()  # latest
        shapesBackup = self.shapesBackups.pop()
        self.shapes = shapesBackup
        self.selectedShapes = []
        for shape in self.shapes:
            shape.selected = False
        self.repaint()

    def setDrawingColor(self, qColor):
        self.drawingLineColor = qColor
        self.drawingRectColor = qColor

    def enterEvent(self, ev):
        self.overrideCursor(self._cursor)

    def focusOutEvent(self, ev):
        self.restoreCursor()

    def leaveEvent(self, ev):
        self.restoreCursor()

    def isVisible(self, shape):
        return self.visible.get(shape, True)

    def createMode(self, value):
        self.createMode = value


    def drawing(self):
        return self.mode == self.CREATE

    def editing(self):
        return self.mode == self.EDIT

    def setEditing(self, value=True):
        self.mode = self.EDIT if value else self.CREATE
        if not value:  # Create
            self.unHighlight()
            self.deSelectShape()
        self.prevPoint = QPointF()  # 浮点坐标
        self.repaint()

    def unHighlight(self):
        if self.hShape:
            self.hShape.highlightClear()
        self.hVertex = self.hShape = None

    def selectedVertex(self):
        return self.hVertex is not None

    # 鼠标移动事件
    def mouseMoveEvent(self, ev):
        """用最后一个点和当前坐标更新线条"""
        pos = self.transformPos(ev.pos())
        # print(pos, 999999999999999)

        # Update coordinates in status bar if image is opened
        # 设置图像RGB值
        try:
            ing_rgb = self.img_rgb_find(pos.x(), pos.y())
        except:
            # print("鼠标像素错误")
            ing_rgb = [0, 0, 0]
        # print("图像RGB",ing_rgb)
        # 设置下标坐标位置
        window = self.parent().window()
        if window.filePath is not None:
            self.parent().window().labelCoordinates.setText(
                'RGB: [%d,%d,%d] X: %d; Y: %d' % (ing_rgb[0], ing_rgb[1], ing_rgb[2], pos.x(), pos.y()))

        # Polygon drawing
        if self.drawing():
            # self.type.shape_type(self.createMode)
            self.overrideCursor(CURSOR_DRAW)  # 绘制鼠标样式
            # print("self.createMode", self.createMode)
            self.line.shape_type = self.createMode
            if self.current:
                # 鼠标当前位置
                color = self.drawingLineColor
                try:
                    if self.outOfPixmap(pos):
                        # 不允许用户在pixmap外绘制
                        # 将点投影到PixMap的边缘
                        pos = self.intersectionPoint(self.current[-1], pos)

                    elif len(self.current) > 1 and self.closeEnough(pos, self.current[0]):
                        # 将线条吸引到起始点并上色以提醒
                        # user:
                        pos = self.current[0]
                        # pos 鼠标当前浮点坐标
                        color = self.current.line_color
                        self.overrideCursor(CURSOR_POINT)  # 鼠标样式
                        self.current.highlightVertex(0, Shape.NEAR_VERTEX)
                except:
                    print("画图失败")
                    pass
                if self.createMode == 'rectangle':
                    self.line[1] = pos
                    self.line.line_color = color
                elif self.createMode == 'polygon':
                    self.line[0] = self.current[-1]
                    self.line[1] = pos
                elif self.createMode == 'flootfill':
                    self.line[1] = self.current[0]
                elif self.createMode == 'eraser':
                    self.line[1] = self.current[0]
                    # self.current[0]当前坐标点
                    # pos 移动坐标点
                    firstpiont = self.current[0]
                    # print("橡皮擦9", firstpiont.x())
                    self.eraser_rgb(firstpiont.x(), firstpiont.y())
                    # 获取起始点坐标的RGBA
                    self.endPoint = pos
                    # self.lastPoint = self.current[-1]
                    # print("橡皮擦移动-1", self.current[0], pos)
                    # 进行重新绘制
                    # self.update()
                self.current.highlightClear()
                self.prevPoint = QPointF()
            else:
                self.prevPoint = pos
            self.repaint()
            return

        # Polygon copy moving
        try:
            if Qt.RightButton & ev.buttons():
                if self.selectedShapeCopy and self.prevPoint:
                    self.overrideCursor(CURSOR_MOVE)
                    self.boundedMoveShape(self.selectedShapeCopy, pos)
                    self.repaint()
                elif self.selectedShape:
                    self.selectedShapeCopy = self.selectedShape.copy()
                    self.repaint()
                return

            # Polygon/Vertex moving
            if Qt.LeftButton & ev.buttons():
                if self.selectedVertex():
                    self.boundedMoveVertex(pos)
                    self.shapeMoved.emit()
                    self.repaint()
                elif self.selectedShape and self.prevPoint:
                    self.overrideCursor(CURSOR_MOVE)
                    self.boundedMoveShape(self.selectedShape, pos)
                    self.shapeMoved.emit()
                    self.repaint()
                return

        except:
            pass

        # 在画布上停留，两种姿势：
        # - Highlight shapes
        # - Highlight vertex
        # 相应地更新形状/顶点填充和工具提示值
        self.setToolTip("Image")
        for shape in reversed([s for s in self.shapes if self.isVisible(s)]):
            # Look for a nearby vertex to highlight. If that fails,
            # check if we happen to be inside a shape.
            index = shape.nearestVertex(pos, self.epsilon)
            if index is not None:
                if self.selectedVertex():
                    self.hShape.highlightClear()
                self.hVertex, self.hShape = index, shape
                shape.highlightVertex(index, shape.MOVE_VERTEX)
                self.overrideCursor(CURSOR_POINT)
                self.setToolTip("Click & drag to move point")
                self.setStatusTip(self.toolTip())
                self.update()
                break
            elif shape.containsPoint(pos):
                if self.selectedVertex():
                    self.hShape.highlightClear()
                self.hVertex, self.hShape = None, shape
                self.setToolTip(
                    "Click & drag to move shape '%s'" % shape.label)
                self.setStatusTip(self.toolTip())
                self.overrideCursor(CURSOR_GRAB)
                self.update()
                break
        else:  # Nothing found, clear highlights, reset state.
            if self.hShape:
                self.hShape.highlightClear()
                self.update()
            self.hVertex, self.hShape = None, None
            self.overrideCursor(CURSOR_DEFAULT)
        # Geoffrey
        if self.isLeftPressed:  # 左键按下
            # print("鼠标左键按下，移动鼠标(canvas)")  # 响应测试语句
            self.endMousePosition = ev.pos() - self.preMousePosition  # 鼠标当前位置-先前位置=单次偏移量
            self.singleOffset = self.singleOffset + self.endMousePosition  # 更新偏移量
            self.preMousePosition = ev.pos()  # 更新当前鼠标在窗口上的位置，下次移动用
            self.repaint()  # 重绘
        # Geoffrey


    def addPointToEdge(self):
        shape = self.prevhShape
        index = self.prevhEdge
        point = self.prevMovePoint
        if shape is None or index is None or point is None:
            return

        shape.insertPoint(index, point)
        shape.highlightVertex(index, shape.MOVE_VERTEX)
        self.hShape = shape
        self.hVertex = index
        self.hEdge = None
        self.movingShape = True

    def removeSelectedPoint(self):
        shape = self.prevhShape
        point = self.prevMovePoint
        if shape is None or point is None:
            return
        index = shape.nearestVertex(point, self.epsilon)
        shape.removePoint(index)
        # shape.highlightVertex(index, shape.MOVE_VERTEX)
        self.hShape = shape
        self.hVertex = None
        self.hEdge = None
        self.movingShape = True  # Save changes

    # 鼠标按下事件
    def mousePressEvent(self, ev):
        # print("self.eraser", self.eraser)
        pos = self.transformPos(ev.pos())
        self.lastPoint = pos
        if ev.button() == Qt.LeftButton:
            # print("左键") # 1 鼠标左键
            if self.drawing():
                print("1",self.current)
                if self.current:
                    # print("2", self.current)
                    # 绘画方形框
                    if self.createMode == 'rectangle':
                        # print("rectangle")
                        self.handleDrawing(pos)

                    elif self.createMode == 'flootfill':
                        # print("flootfill")
                        leftTop = self.line[1]
                        leftTop.x()
                        self.fill(leftTop.x(), leftTop.y())
                        self.finalise()
                        # print("<><><>")
                    elif self.createMode == 'polygon':
                        self.current.addPoint(self.line[1])
                        self.line[0] = self.current[-1]
                        self.line[1] = pos
                        # print("polygon")
                    elif self.createMode == 'eraser':
                        # self.eraser_rgb(ev)
                        # print("橡皮擦按下")
                        self.lastPoint = pos
                        self.endPoint = self.lastPoint
                        # 设置RGBA获取函数
            else:
                # print("绘画失败")
                self.selectShapePoint(pos)
                self.prevPoint = pos
                self.repaint()
            # Geoffrey
            self.isLeftPressed = True;  # 左键按下(图片被点住),置Ture
            self.preMousePosition = ev.pos()  # 获取鼠标当前位置
            # Geoffrey
        elif ev.button() == Qt.RightButton and self.editing():
            self.selectShapePoint(pos)  # 绘画方形框
            self.prevPoint = pos
            self.repaint()



    # 鼠标释放事件
    def mouseReleaseEvent(self, ev):
        if ev.button() == Qt.RightButton:  # 右键点击
            menu = self.menus[bool(self.selectedShapeCopy)]
            self.restoreCursor()
            if not menu.exec_(self.mapToGlobal(ev.pos())) \
                    and self.selectedShapeCopy:
                # Cancel the move by deleting the shadow copy.
                self.selectedShapeCopy = None
                self.repaint()
        elif ev.button() == Qt.LeftButton and self.selectedShape:
            if self.selectedVertex():
                self.overrideCursor(CURSOR_POINT)
            else:
                self.overrideCursor(CURSOR_GRAB)
            if (
                self.editing()
                and int(ev.modifiers()) == Qt.ShiftModifier
            ):
                # Add point to line if: left-click + SHIFT on a line segment
                self.addPointToEdge()
        elif ev.button() == Qt.LeftButton:
            pos = self.transformPos(ev.pos())
            if self.drawing():
                if self.createMode == "rectangle":
                    self.handleDrawing(pos)
                elif self.createMode == "polygon":
                    self.handleDrawingpolygon(pos)
                elif self.createMode == "flootfill":
                    self.handleDrawingpolygon(pos)
                elif self.createMode == 'eraser':
                    print("橡皮擦释放")
                    self.handleDrawingeraser(pos)
                    # self.endPoint = pos

                    # 进行重新绘制
            if (
                self.editing()
                and int(ev.modifiers()) == Qt.ShiftModifier
            ):
                # Delete point if: left-click + SHIFT on a point
                self.removeSelectedPoint()

        if self.movingShape and self.hShape:
            index = self.shapes.index(self.hShape)
            if (
                self.shapesBackups[-1][index].points
                != self.shapes[index].points
            ):
                self.storeShapes()
                self.shapeMoved.emit()

            self.movingShape = False


    def endMove(self, copy=False):
        assert self.selectedShape and self.selectedShapeCopy
        shape = self.selectedShapeCopy
        # del shape.fill_color
        # del shape.line_color
        if copy:
            self.shapes.append(shape)
            self.selectedShape.selected = False
            self.selectedShape = shape
            self.repaint()
        else:
            self.selectedShape.points = [p for p in shape.points]
        self.selectedShapeCopy = None

    def hideBackroundShapes(self, value):
        self.hideBackround = value
        if self.selectedShape:
            # Only hide other shapes if there is a current selection.
            # Otherwise the user will not be able to select a shape.
            self.setHiding(True)
            self.repaint()

    def handleDrawing(self, pos):
        """手绘标注"""
        if self.current and self.current.reachMaxPoints() is False:
            initPos = self.current[0]
            minX = initPos.x()
            minY = initPos.y()
            targetPos = self.line[1]
            maxX = targetPos.x()
            maxY = targetPos.y()
            self.current.addPoint(QPointF(maxX, minY))
            self.current.addPoint(targetPos)
            self.current.addPoint(QPointF(minX, maxY))
            self.finalise()
        elif not self.outOfPixmap(pos):
            self.current = Shape()
            self.current.addPoint(pos)
            self.line.points = [pos, pos]
            self.setHiding()
            self.drawingPolygon.emit(True)
            self.update()

    def handleDrawingpolygon(self, pos):
        """手绘多边形"""
        if self.current:
            self.current.addPoint(self.current[0])
            # print("添加坐标2222", self.current[0])
            # self.finalise()
        elif not self.outOfPixmap(pos):
            self.current = Shape()
            self.current.addPoint(pos)
            self.line.points = [pos, pos]
            self.setHiding()
            self.drawingPolygon.emit(True)
            self.update()

    def handleDrawingeraser(self, pos):
        """手绘橡皮擦"""
        if self.current:
            self.current.addPoint(self.current[0])
            # print("添加坐标2222", self.current[0])
            # self.finalise()
        elif not self.outOfPixmap(pos):
            self.current = Shape()
            self.current.addPoint(pos)
            self.line.points = [pos, pos]
            self.setHiding()
            self.drawingPolygon.emit(True)
            self.endPoint = pos
            # self.update()

    def setHiding(self, enable=True):
        self._hideBackround = self.hideBackround if enable else False

    def canCloseShape(self):
        return self.drawing() and self.current and len(self.current) > 2

    def mouseDoubleClickEvent(self, ev):
        # We need at least 4 points here, since the mousePress handler
        # adds an extra one before this handler is called.
        if self.canCloseShape() and len(self.current) > 3:
            self.current.popPoint()
            self.finalise()

    def selectShape(self, shape):
        self.deSelectShape()
        shape.selected = True
        self.selectedShape = shape
        self.setHiding()
        self.selectionChanged.emit(True)
        self.update()

    def selectShapePoint(self, point):
        """选择创建的包含此点的第一个形状"""
        self.deSelectShape()
        if self.selectedVertex():  # A vertex is marked for selection.
            index, shape = self.hVertex, self.hShape
            shape.highlightVertex(index, shape.MOVE_VERTEX)
            self.selectShape(shape)
            return
        for shape in reversed(self.shapes):
            if self.isVisible(shape) and shape.containsPoint(point):
                self.selectShape(shape)
                self.calculateOffsets(shape, point)
                return

    def calculateOffsets(self, shape, point):
        rect = shape.boundingRect()
        x1 = rect.x() - point.x()
        y1 = rect.y() - point.y()
        x2 = (rect.x() + rect.width()) - point.x()
        y2 = (rect.y() + rect.height()) - point.y()
        self.offsets = QPointF(x1, y1), QPointF(x2, y2)

    def boundedMoveVertex(self, pos):
        index, shape = self.hVertex, self.hShape
        point = shape[index]
        if self.outOfPixmap(pos):
            pos = self.intersectionPoint(point, pos)
        shiftPos = pos - point

        if self.createMode in ['polygon', 'flootfill', "eraser"]:
            shape.moveVertexBy(index, shiftPos)
        elif self.createMode == "rectangle":
            shape.moveVertexBy(index, shiftPos)
            lindex = (index + 1) % 4
            rindex = (index + 3) % 4
            lshift = None
            rshift = None
            if index % 2 == 0:
                rshift = QPointF(shiftPos.x(), 0)
                lshift = QPointF(0, shiftPos.y())
            else:
                lshift = QPointF(shiftPos.x(), 0)
                rshift = QPointF(0, shiftPos.y())
            shape.moveVertexBy(rindex, rshift)
            shape.moveVertexBy(lindex, lshift)

    def boundedMoveShape(self, shape, pos):
        if self.outOfPixmap(pos):
            return False  # No need to move
        o1 = pos + self.offsets[0]
        if self.outOfPixmap(o1):
            pos -= QPointF(min(0, o1.x()), min(0, o1.y()))
        o2 = pos + self.offsets[1]
        if self.outOfPixmap(o2):
            pos += QPointF(min(0, self.pixmap.width() - o2.x()),
                           min(0, self.pixmap.height() - o2.y()))
        # 下一行跟踪光标相对于形状的新位置，但也会使其在接近边界时有点“不稳定”，
        # 并允许其出于某种原因超出形状的区域。
        # self.calculateOffsets(self.selectedShape, pos)
        dp = pos - self.prevPoint
        if dp:
            shape.moveBy(dp)
            self.prevPoint = pos
            return True
        return False

    def deSelectShape(self):
        if self.selectedShape:
            self.selectedShape.selected = False
            self.selectedShape = None
            self.setHiding(False)
            self.selectionChanged.emit(False)
            self.update()

    def deleteSelected(self):
        if self.selectedShape:
            shape = self.selectedShape
            self.shapes.remove(self.selectedShape)
            self.selectedShape = None
            self.update()
            return shape

    def copySelectedShape(self):
        if self.selectedShape:
            shape = self.selectedShape.copy()
            self.deSelectShape()
            self.shapes.append(shape)
            shape.selected = True
            self.selectedShape = shape
            self.boundedShiftShape(shape)
            return shape

    def boundedShiftShape(self, shape):
        # Try to move in one direction, and if it fails in another.
        # Give up if both fail.
        point = shape[0]
        offset = QPointF(2.0, 2.0)
        self.calculateOffsets(shape, point)
        self.prevPoint = point
        if not self.boundedMoveShape(shape, point - offset):
            self.boundedMoveShape(shape, point + offset)

    # 绘制事件
    def paintEvent(self, event):
        if not self.pixmap:
            return super(Canvas, self).paintEvent(event)

        p = self._painter
        p.begin(self)
        # Geoffrey
        p.drawPixmap(self.singleOffset, self.scaledImg)  # 从图像文件提取Pixmap并显示在指定位置
        # Geoffrey
        p.setRenderHint(QPainter.Antialiasing)
        p.setRenderHint(QPainter.HighQualityAntialiasing)
        p.setRenderHint(QPainter.SmoothPixmapTransform)
        # 反走样,画布开始的操作

        p.scale(self.scale, self.scale)
        p.translate(self.offsetToCenter())

        p.drawPixmap(0, 0, self.pixmap)
        Shape.scale = self.scale
        for shape in self.shapes:
            if (shape.selected or not self._hideBackround) and self.isVisible(shape):
                shape.fill = shape.selected or shape == self.hShape
                shape.paint(p)
        if self.current:
            self.current.paint(p)
            self.line.paint(p)
        if self.selectedShapeCopy:
            self.selectedShapeCopy.paint(p)

        # Paint rect 创建开始的绘制矩形
        if self.createMode == 'rectangle' and self.current is not None and len(self.line) == 2:
            leftTop = self.line[0]
            rightBottom = self.line[1]
            # print("橡皮擦-10",self.line[0],self.line[1])
            rectWidth = rightBottom.x() - leftTop.x()
            rectHeight = rightBottom.y() - leftTop.y()
            p.setPen(QPen(self.drawingRectColor, 1))  # 开始画框的边框颜色和大小
            brush = QBrush(Qt.BDiagPattern)
            p.setBrush(brush)
            p.drawRect(leftTop.x(), leftTop.y(), rectWidth, rectHeight)

        if self.createMode == 'polygon' and self.current is not None:
            # drawing_shape = self.current  # ???
            # print("drawing_shape", self.current[0], self.current[-1])
            # drawing_shape.addPoint(self.line[0])
            # print("添加坐标3333", self.line[0])
            # drawing_shape.fill = True
            # drawing_shape.fill_color.setAlpha(64)
            # drawing_shape.paint(p)
            pass

        if self.createMode == 'flootfill' and self.current is not None:
            pass
            # print("点击")
            # leftTop = self.current[0]
            # leftTop.x()
            # a = []
            # print("点1",leftTop.x(),leftTop.y())
            # a = self.fill(leftTop.x(), leftTop.y())
            # # self.finalise()
            # # self.newShape.emit()
            # print("结束", a)

        #  橡皮擦
        if self.createMode == 'eraser' and self.current is not None:
            pp = QPainter(self.pixmap)
            # print("绘画endPoint", self.r, self.g, self.b, self.a)
            pen = QPen(QColor(self.r, self.g, self.b, self.a))  # 定义笔格式对象
            pen.setWidth(20)  # 设置笔的宽度
            pp.setPen(pen)  # 将笔格式赋值给 画笔
            # 根据鼠标指针前后两个位置绘制直线
            pp.drawLine(self.lastPoint, self.endPoint)
            # 让前一个坐标值等于后一个坐标值，
            # 这样就能实现画出连续的线
            self.lastPoint = self.endPoint
            self.pixmap.save("mask_img/eraser.png")

        # 鼠标画框线
        if self.drawing() and not self.prevPoint.isNull() and not self.outOfPixmap(self.prevPoint):
            p.setPen(QColor(0, 0, 0))
            p.drawLine(self.prevPoint.x(), 0, self.prevPoint.x(), self.pixmap.height())
            p.drawLine(0, self.prevPoint.y(), self.pixmap.width(), self.prevPoint.y())

        # 背景颜色
        self.setAutoFillBackground(True)
        if self.verified:
            pal = self.palette()
            pal.setColor(self.backgroundRole(), QColor(184, 239, 38, 128))
            self.setPalette(pal)
        else:
            pal = self.palette()
            pal.setColor(self.backgroundRole(), QColor(232, 232, 232, 255))
            self.setPalette(pal)
        p.end()

    def transformPos(self, point):
        """从widget逻辑坐标转换为painter逻辑坐标"""
        return point / self.scale - self.offsetToCenter()

    def offsetToCenter(self):
        s = self.scale
        area = super(Canvas, self).size()
        w, h = self.pixmap.width() * s, self.pixmap.height() * s
        aw, ah = area.width(), area.height()
        x = (aw - w) / (2 * s) if aw > w else 0
        y = (ah - h) / (2 * s) if ah > h else 0
        return QPointF(x, y)

    def outOfPixmap(self, p):
        w, h = self.pixmap.width(), self.pixmap.height()
        return not (0 <= p.x() <= w and 0 <= p.y() <= h)

    def finalise(self):
        """最终结束标注，弹出标签对话框"""
        assert self.current
        if self.current.points[0] == self.current.points[-1]:
            self.current = None
            self.drawingPolygon.emit(False)  # 发送信号
            self.update()
            # 设置取消提示
            return
        # print("弹出标签对话框")
        self.current.close()
        self.shapes.append(self.current)
        self.storeShapes()
        self.current = None
        self.setHiding(False)
        self.newShape.emit()  # 弹出标签对话框
        self.update()

    # 足够近
    def closeEnough(self, p1, p2):
        # d = distance(p1 - p2)
        # m = (p1-p2).manhattanLength()
        # print "d %.2f, m %d, %.2f" % (d, m, d - m)
        return distance(p1 - p2) < self.epsilon

    # 交叉点，处理画框边界问题
    def intersectionPoint(self, p1, p2):
        # 以顺时针方式循环通过每个图像边缘
        # 找到与当前线段相交的线段
        # http://paulbourke.net/geometry/lineline2d/
        size = self.pixmap.size()
        points = [(0, 0),
                  (size.width(), 0),
                  (size.width(), size.height()),
                  (0, size.height())]
        x1, y1 = p1.x(), p1.y()
        x2, y2 = p2.x(), p2.y()
        d, i, (x, y) = min(self.intersectingEdges((x1, y1), (x2, y2), points))
        x3, y3 = points[i]
        x4, y4 = points[(i + 1) % 4]
        if (x, y) == (x1, y1):
            # 处理前一点位于其中一条边上的情况
            if x3 == x4:
                return QPointF(x3, min(max(0, y2), max(y3, y4)))
            else:  # y3 == y4
                return QPointF(min(max(0, x2), max(x3, x4)), y3)
        return QPointF(x, y)

    def intersectingEdges(self, x1y1, x2y2, points):
        """For each edge formed by `points', yield the intersection
        with the line segment `(x1,y1) - (x2,y2)`, if it exists.
        Also return the distance of `(x2,y2)' to the middle of the
        edge along with its index, so that the one closest can be chosen."""
        x1, y1 = x1y1
        x2, y2 = x2y2
        for i in range(4):
            x3, y3 = points[i]
            x4, y4 = points[(i + 1) % 4]
            denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1)
            nua = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)
            nub = (x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)
            if denom == 0:
                # This covers two cases:
                #   nua == nub == 0: Coincident
                #   otherwise: Parallel
                continue
            ua, ub = nua / denom, nub / denom
            if 0 <= ua <= 1 and 0 <= ub <= 1:
                x = x1 + ua * (x2 - x1)
                y = y1 + ua * (y2 - y1)
                m = QPointF((x3 + x4) / 2, (y3 + y4) / 2)
                d = distance(m - QPointF(x2, y2))
                yield d, i, (x, y)

    # These two, along with a call to adjustSize are required for the
    # scroll area.
    def sizeHint(self):
        return self.minimumSizeHint()

    def minimumSizeHint(self):
        """最小尺寸"""
        if self.pixmap:
            return self.scale * self.pixmap.size()
        return super(Canvas, self).minimumSizeHint()

    def wheelEvent(self, ev):
        qt_version = 4 if hasattr(ev, "delta") else 5
        if qt_version == 4:
            if ev.orientation() == Qt.Vertical:
                v_delta = ev.delta()
                h_delta = 0
            else:
                h_delta = ev.delta()
                v_delta = 0
        else:
            delta = ev.angleDelta()
            h_delta = delta.x()
            v_delta = delta.y()

        mods = ev.modifiers()
        if Qt.ControlModifier == int(mods) and v_delta:
            self.zoomRequest.emit(v_delta)
        else:
            v_delta and self.scrollRequest.emit(v_delta, Qt.Vertical)
            h_delta and self.scrollRequest.emit(h_delta, Qt.Horizontal)
        ev.accept()

    def keyPressEvent(self, ev):
        """按键事件"""
        key = ev.key()
        if key == Qt.Key_Escape and self.current:
            print('ESC press')
            self.current = None
            self.drawingPolygon.emit(False)
            self.update()
        elif key == Qt.Key_Return and self.canCloseShape():
            self.finalise()
        elif key == Qt.Key_Left and self.selectedShape:
            self.moveOnePixel('Left')
        elif key == Qt.Key_Right and self.selectedShape:
            self.moveOnePixel('Right')
        elif key == Qt.Key_Up and self.selectedShape:
            self.moveOnePixel('Up')
        elif key == Qt.Key_Down and self.selectedShape:
            self.moveOnePixel('Down')

    def moveOnePixel(self, direction):
        """移动一个像素"""
        # print(self.selectedShape.points)
        if direction == 'Left' and not self.moveOutOfBound(QPointF(-1.0, 0)):
            # print("move Left one pixel")
            self.selectedShape.points[0] += QPointF(-1.0, 0)
            self.selectedShape.points[1] += QPointF(-1.0, 0)
            self.selectedShape.points[2] += QPointF(-1.0, 0)
            self.selectedShape.points[3] += QPointF(-1.0, 0)
        elif direction == 'Right' and not self.moveOutOfBound(QPointF(1.0, 0)):
            # print("move Right one pixel")
            self.selectedShape.points[0] += QPointF(1.0, 0)
            self.selectedShape.points[1] += QPointF(1.0, 0)
            self.selectedShape.points[2] += QPointF(1.0, 0)
            self.selectedShape.points[3] += QPointF(1.0, 0)
        elif direction == 'Up' and not self.moveOutOfBound(QPointF(0, -1.0)):
            # print("move Up one pixel")
            self.selectedShape.points[0] += QPointF(0, -1.0)
            self.selectedShape.points[1] += QPointF(0, -1.0)
            self.selectedShape.points[2] += QPointF(0, -1.0)
            self.selectedShape.points[3] += QPointF(0, -1.0)
        elif direction == 'Down' and not self.moveOutOfBound(QPointF(0, 1.0)):
            # print("move Down one pixel")
            self.selectedShape.points[0] += QPointF(0, 1.0)
            self.selectedShape.points[1] += QPointF(0, 1.0)
            self.selectedShape.points[2] += QPointF(0, 1.0)
            self.selectedShape.points[3] += QPointF(0, 1.0)
        self.shapeMoved.emit()
        self.repaint()

    def moveOutOfBound(self, step):
        """移出限制"""
        points = [p1 + p2 for p1, p2 in zip(self.selectedShape.points, [step] * 4)]
        return True in map(self.outOfPixmap, points)

    def setLastLabel(self, text, line_color=None, fill_color=None):
        """设置最后一个标签"""
        assert text
        self.shapes[-1].label = text
        if line_color:
            self.shapes[-1].line_color = line_color

        if fill_color:
            self.shapes[-1].fill_color = fill_color

        return self.shapes[-1]

    def undoLastPoint(self):
        print(self.current, self.current.isClosed(), 88888888888888)
        if not self.current or self.current.isClosed():
            return
        self.current.popPoint()
        if len(self.current) > 0:
            self.line[0] = self.current[-1]
        else:
            self.current = None
            self.drawingPolygon.emit(False)
        self.repaint()

    def undoLastLine(self):
        """撤消最后一行"""
        assert self.shapes
        self.current = self.shapes.pop()
        self.current.setOpen()
        # self.line.points = [self.current[-1], self.current[0]]
        if self.createMode in ["polygon", "linestrip"]:
            self.line.points = [self.current[-1], self.current[0]]
        elif self.createMode in ["rectangle", "line", "circle"]:
            self.current.points = self.current.points[0:1]
        self.drawingPolygon.emit(True)


    def resetAllLines(self):
        """重置所有行"""
        assert self.shapes
        self.current = self.shapes.pop()
        self.current.setOpen()
        self.line.points = [self.current[-1], self.current[0]]
        self.drawingPolygon.emit(True)
        self.current = None
        self.drawingPolygon.emit(False)
        self.update()

    def loadPixmap(self, pixmap):
        """加载图片"""
        self.pixmap = pixmap
        # print("加载图片self.pixmap", self.pixmap)
        self.shapes = []
        self.repaint()

    # def loadShapes(self, shapes):
    #     self.shapes = list(shapes)
    #     self.current = None
    #     self.repaint()

    def loadShapes(self, shapes, replace=True):
        if replace:
            self.shapes = list(shapes)
        else:
            self.shapes.extend(shapes)
        self.storeShapes()
        self.current = None
        self.hShape = None
        self.hVertex = None
        self.hEdge = None
        self.repaint()


    def setShapeVisible(self, shape, value):
        """设置形状可见"""
        self.visible[shape] = value
        self.repaint()

    def currentCursor(self):
        cursor = QApplication.overrideCursor()
        if cursor is not None:
            cursor = cursor.shape()
        return cursor

    def overrideCursor(self, cursor):
        self._cursor = cursor
        if self.currentCursor() is None:
            QApplication.setOverrideCursor(cursor)
        else:
            QApplication.changeOverrideCursor(cursor)

    def restoreCursor(self):
        QApplication.restoreOverrideCursor()

    def resetState(self):
        self.restoreCursor()
        self.pixmap = None
        self.update()

    def fill(self, x, y):
        """漫水填充"""
        try:
            f = open("temp_imagepath.txt", "r")  # 设置文件对象
            str = f.read()  # 将txt文件的所有内容读入到字符串str中
            file = open("temp_RGB.txt", "r")  # 设置文件对象
            str_errra = file.read()
            if str == str_errra:
                img = cv2.imread(str_errra, True)
            else:
                img = cv2.imread(str, True)
            img1 = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
            # print("自动标注-1",x,y)
            fill_point = img1[int(x), int(y)]
            fill_point1 = img[int(x), int(y)]
            img = img.copy()
            cv2.threshold(img, fill_point, 0, cv2.THRESH_TOZERO_INV, img)
            cv2.threshold(img, fill_point - 1, 255, cv2.THRESH_BINARY, img)
            cv2.imshow("img", img)
            cv2.waitKey(0)
            # print("自动标注-3", fill_point)
            img1 = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
            contours, hierarchy = cv2.findContours(img1, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
            # print("contours", contours)
            # print("hierarchy", hierarchy)
            if contours:
                a = []
                for g in range(np.array(contours).shape[0]):
                    for i in contours[g]:
                        for j in i:
                            a.append(tuple(j))
                # print("点集", a, type(a))
                del a[0]
                # print(len(a))
                a1 = []
                for t in range(len(a)):
                    if t % 4 == 0:
                        a1.append(a[t])
                # print("a1", a1)
                print("长度", len(a1))
                for p in a1:
                    p1 = list(p)
                    # print("填充结束1", p, QPointF(p1[0], p1[1]))
                    self.current.addPoint(QPointF(p1[0], p1[1]))  # !!!
            else:
                print("没有值")
        except:
            print("error")

    def automaticfill(self):
        if self.drawing():
            self.fill(210, 210)

    def eraser_rgb(self, x, y):
        # print("橡皮擦mask",self.maskDirPath_if)
        try:
            if self.maskDirPath_if:
                img_path5 = "mask_img/mask.png"
            else:
                with open("./temp_imagepath.txt", 'r') as file:
                    for img_path5 in file.readlines():
                        pass
            im = Image.open(img_path5)
            pix = im.load()
            img_RGBA = pix[x, y]
            if isinstance(img_RGBA, tuple):
                if len(img_RGBA) == 4:
                    self.r, self.g, self.b, self.a = img_RGBA
                elif len(img_RGBA) == 3:
                    self.r, self.g, self.b = img_RGBA
            else:
                self.r = img_RGBA
                self.g = img_RGBA
                self.b = img_RGBA
            file = open("temp_RGB.txt", 'w')
            file.write(img_path5)
        except:
            print("橡皮擦失败1")

    def img_rgb_find(self, x, y):
        f = open("temp_imagepath.txt", "r")  # 设置文件对象
        str = f.read()  # 将txt文件的所有内容读入到字符串str中
        img = cv2.imread(str, True)
        rgb_point1 = img[int(x), int(y)]
        rgb_point = list(rgb_point1)
        # print("像素查找", rgb_point, type(rgb_point))
        return rgb_point
